package io.gitee.zhangbinhub.admin.oauth.domain

import io.gitee.zhangbinhub.acp.boot.exceptions.ServerException
import io.gitee.zhangbinhub.acp.core.CommonTools
import io.gitee.zhangbinhub.admin.common.tools.TokenTools
import io.gitee.zhangbinhub.admin.oauth.base.OauthBaseDomain
import io.gitee.zhangbinhub.admin.oauth.entity.Application
import io.gitee.zhangbinhub.admin.oauth.po.ApplicationPo
import io.gitee.zhangbinhub.admin.oauth.po.ApplicationQueryPo
import io.gitee.zhangbinhub.admin.oauth.repo.ApplicationRepository
import io.gitee.zhangbinhub.admin.oauth.repo.UserRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Page
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import javax.persistence.criteria.Predicate

/**
 * @author zhang by 13/01/2019
 * @since JDK 11
 */
@Service
@Transactional(readOnly = true)
class ApplicationDomain @Autowired
constructor(
    userRepository: UserRepository,
    private val tokenTools: TokenTools,
    private val applicationRepository: ApplicationRepository
) :
    OauthBaseDomain(userRepository) {

    fun getAppList(): MutableList<Application> = applicationRepository.findAllByOrderByIdentifyAscAppNameAsc()

    @Throws(ServerException::class)
    fun getOwnAppList(bearerTokenAuthentication: BearerTokenAuthentication): MutableList<Application> {
        val currUserInfo = getUserInfoByLoginNo(bearerTokenAuthentication.name)
            ?: throw ServerException("无法获取当前用户信息")
        return if (isSuper(currUserInfo)) {
            applicationRepository.findAllByOrderByIdentifyAscAppNameAsc()
        } else {
            applicationRepository.findById(
                tokenTools.getAuthenticatedPrincipal(bearerTokenAuthentication).clientId ?: ""
            ).let {
                if (it.isPresent) {
                    mutableListOf(it.get())
                } else {
                    mutableListOf()
                }
            }
        }
    }

    @Transactional
    fun doCreate(applicationPo: ApplicationPo): Application =
        Application(
            appName = applicationPo.appName!!,
            secret = CommonTools.getUuid32(),
            scope = applicationPo.scope?.trim()?.replace("，", ",") ?: "",
            identify = applicationPo.identify?.trim() ?: "",
            accessTokenValiditySeconds = applicationPo.accessTokenValiditySeconds,
            refreshTokenValiditySeconds = applicationPo.refreshTokenValiditySeconds,
            covert = true
        ).let {
            applicationRepository.save(it)
        }

    @Transactional
    @Throws(ServerException::class)
    fun doUpdate(applicationPo: ApplicationPo): Application =
        applicationRepository.getReferenceById(applicationPo.id!!).copy(
            appName = applicationPo.appName!!,
            scope = applicationPo.scope?.trim()?.replace("，", ",") ?: "",
            identify = applicationPo.identify?.trim() ?: "",
            accessTokenValiditySeconds = applicationPo.accessTokenValiditySeconds,
            refreshTokenValiditySeconds = applicationPo.refreshTokenValiditySeconds
        ).let {
            applicationRepository.save(it)
        }

    @Transactional
    @Throws(ServerException::class)
    fun doUpdateSecret(appId: String): Application =
        applicationRepository.getReferenceById(appId).copy(
            secret = CommonTools.getUuid32()
        ).let {
            applicationRepository.save(it)
        }

    @Transactional
    fun doDelete(idList: MutableList<String>) = applicationRepository.deleteByIdInAndCovert(idList, true)

    fun doQuery(applicationQueryPo: ApplicationQueryPo): Page<Application> =
        applicationRepository.findAll({ root, _, criteriaBuilder ->
            val predicateList: MutableList<Predicate> = mutableListOf()
            if (!CommonTools.isNullStr(applicationQueryPo.appName)) {
                predicateList.add(
                    criteriaBuilder.like(
                        root.get<Any>("appName").`as`(String::class.java),
                        "%" + applicationQueryPo.appName + "%"
                    )
                )
            }
            criteriaBuilder.and(*predicateList.toTypedArray())
        }, buildPageRequest(applicationQueryPo.queryParam!!))

    fun getApp(appId: String): Application? = applicationRepository.findById(appId).orElse(null)

}
